package com.zoe.onelink.auth.client.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.zoe.onelink.auth.client.AuthRemoteClient;
import com.zoe.onelink.auth.properties.AuthProperties;
import com.zoe.onelink.common.constant.OnelinkFeignConstant;
import com.zoe.onelink.common.model.ApiResult;
import com.zoe.onelink.upms.common.constants.SecurityConstants;
import com.zoe.onelink.upms.common.constants.SystemConstants;
import com.zoe.onelink.upms.common.entity.AuthInfo;
import com.zoe.onelink.upms.common.service.OneLinkTokenStore;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2018</p>
 * <p>公司: 智业软件</p>
 *
 * @author LinHongKai
 * @date 2022-07-13 17:16
 */
@Slf4j
@RequiredArgsConstructor
public class AuthHttpClient implements AuthRemoteClient {

    private final OneLinkTokenStore oneLinkTokenStore;

    private final AuthProperties authProperties;


    @Override
    public ApiResult auth(String username, String password) {
        if (StrUtil.isEmpty(authProperties.getAuthUrl())) {
            log.error("该服务尚未连接nacos，请连接nacos，或者通过onelink.auth.authUrl配置项来手动指定授权服务器地址");
            throw new RuntimeException("该服务尚未连接nacos，请连接nacos，请通过onelink.auth.authUrl配置项来手动指定授权服务器地址");
        }
        String url = authProperties.getAuthUrl() + "/oauth/token";
        Map<String, Object> paramsMap = new HashMap<>();
        paramsMap.put("username", username);
        paramsMap.put("password", password);
        paramsMap.put(SystemConstants.REST_TEMPLATE_PARAM_NAME, SystemConstants.REST_TEMPLATE_PARAM_VALUE);
        log.info("向授权服务器进行授权认证，url：{}，paramMap：{}", url, paramsMap);
        String apiResultStr = HttpUtil.get(url, paramsMap);
        log.info("向授权服务器进行授权认证，返回结果：{}", apiResultStr);
        return JSON.parseObject(apiResultStr, ApiResult.class);
    }


    @Override
    public OAuth2Authentication readAuthentication(String accessToken) {
        // 1. 从远端授权服务器中，获取用户的授权认证信息
        if (StrUtil.isEmpty(authProperties.getAuthUrl())) {
            log.error("该服务尚未连接nacos，请连接nacos，或者通过onelink.auth.authUrl配置项来手动指定授权服务器地址");
            throw new RuntimeException("该服务尚未连接nacos，请连接nacos，请通过onelink.auth.authUrl配置项来手动指定授权服务器地址");
        }
        String url = authProperties.getAuthUrl() + SecurityConstants.ENDPOINT_AUTH_READ_AUTHENTICATION +
                "?accessToken=" + accessToken;
        log.info("向授权服务器获取AuthInfo，url：{}", url);
        ApiResult<String> apiResult = null;
        try{
            String apiResultStr = HttpUtil.get(url);
            apiResult = JSON.parseObject(apiResultStr, ApiResult.class);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        log.info("向授权服务器获取AuthInfo，返回结果：{}", apiResult);
        if(apiResult == null || !apiResult.checkSuccess()){
            return null;
        }
        AuthInfo authInfo = oneLinkTokenStore.deserialize(apiResult.getData(), AuthInfo.class);
        if(authInfo == null) {
            return null;
        }
        OAuth2Authentication oAuth2Authentication = authInfo.buildAuthentication();

        // 2. 从远端授权服务器中，获取权限标识集合
        url = authProperties.getAuthUrl() + SecurityConstants.ENDPOINT_AUTH_READ_PERMISSIONS +
                        "?accessToken=" + accessToken;
        log.info("向授权服务器获取Permissions，url：{}", url);
        ApiResult<List<String>> apiResult2 = null;
        try{
            String apiResultStr = HttpUtil.get(url);
            apiResult2 = JSON.parseObject(apiResultStr, ApiResult.class);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        log.info("向授权服务器获取Permissions，返回结果：{}", apiResult2);
        List<String> permissions = apiResult2 == null || !apiResult2.checkSuccess() ? null : apiResult2.getData();

        // 3. 更新本地redis的用户授权认证信息
        // 若本地redis还遗留着旧的token，要将旧的token删除
        OAuth2AccessToken existingAccessToken = oneLinkTokenStore.getAccessToken(oAuth2Authentication);
        if (existingAccessToken != null) {
            oneLinkTokenStore.removeAccessToken(existingAccessToken);
        }

        // 4. 将用户的认证信息，同步至本服务redis
        oneLinkTokenStore.flushAuthInfo(authInfo);

        // 5. 将用户的权限标识集合，同步至本服务redis
        oneLinkTokenStore.flushPermission(authInfo.getAccessToken(), permissions);

        return oAuth2Authentication;
    }


    @Override
    public Boolean redisIsEqual(String serviceId, String redisKey, String redisValue) {
        if (StrUtil.isEmpty(authProperties.getAuthUrl())) {
            log.error("该服务尚未连接nacos，请连接nacos，或者通过onelink.auth.authUrl配置项来手动指定授权服务器地址");
            throw new RuntimeException("该服务尚未连接nacos，请连接nacos，请通过onelink.auth.authUrl配置项来手动指定授权服务器地址");
        }
        String url =
                authProperties.getAuthUrl() + SecurityConstants.ENDPOINT_AUTH_REDIS_IS_EQUAL +
                        "?serviceId=" + serviceId + "&redisKey=" + redisKey + "&redisValue=" + redisValue;
        log.info("向授权服务器比对redis信息，url：{}", url);
        ApiResult<Boolean> apiResult = null;
        try{
            String apiResultStr = HttpUtil.get(url);
            apiResult = JSON.parseObject(apiResultStr, ApiResult.class);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        log.info("向授权服务器比对redis信息，返回结果：{}", apiResult);
        if(apiResult == null || !apiResult.checkSuccess()){
            return null;
        }
        return apiResult.getData();
    }
}
